package com.eeb.dropwizardmongo.example.resources;

import com.codahale.metrics.annotation.Timed;
import com.eeb.dropwizardmongo.example.api.Track;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.mongojack.DBQuery;
import org.mongojack.DBUpdate;
import org.mongojack.JacksonDBCollection;

import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.Map;

@Path("/tracks")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class TracksResource {

    private JacksonDBCollection<Track, String> tracks;

    public TracksResource(JacksonDBCollection<Track, String> tracks) {
        this.tracks = tracks;
    }

    @GET
    @Produces(value = MediaType.APPLICATION_JSON)
    @Timed
    public Response getTracks(@QueryParam("org") String org,
                              @QueryParam("assignment") String assignment,
                              @QueryParam("badge") String badge) throws UnsupportedEncodingException {
        DBQuery.Query query = DBQuery.empty();
        if (org != null)
            query = org.startsWith("-") ?
                    query.notEquals("organization", URLEncoder.encode(org.substring(1), "UTF-8")) :
                    query.is("organization", URLEncoder.encode(org, "UTF-8"));
        if (assignment != null)
            query = assignment.startsWith("-") ?
                    query.nor(DBQuery.all("assignments", URLEncoder.encode(assignment.substring(1), "UTF-8"))) :
                    query.all("assignments", URLEncoder.encode(assignment, "UTF-8"));
        if (badge != null)
            query = badge.startsWith("-") ?
                    query.nor(DBQuery.all("badges", URLEncoder.encode(badge.substring(1), "UTF-8"))) :
                    query.all("badges", URLEncoder.encode(badge, "UTF-8"));
        return Response.ok(tracks.find(query).toArray()).build();
    }

    @Path("/{track}")
    @GET
    @Produces(value = MediaType.APPLICATION_JSON)
    @Timed
    public Response getOrg(@PathParam("track") String track) {
        return Response.ok(tracks.findOne(DBQuery.is("name", track))).build();
    }

    @Path("/{track}/assignments")
    @GET
    @Produces(value = MediaType.APPLICATION_JSON)
    @Timed
    public Response getAssignments(@PathParam("track") String track) {
        return Response.ok(tracks.findOne(DBQuery.is("name", track)).getAssignments()).build();
    }

    @POST
    @Timed
    public Response postOrg(@Valid Track track) throws URISyntaxException, UnsupportedEncodingException {
        URI uri = new URI("/" + track.getName());
        if (tracks.findOne(DBQuery.is("name", track.getName())) == null)
            tracks.insert(track);
        return Response.created(uri).entity(tracks.findOne(DBQuery.is("name", track.getName()))).build();
    }

    @PUT
    @Timed
    public Response putOrg(@Valid Track track) {
        return Response.status(405).entity("PUT to /tracks/{name} or POST to /tracks").build();
    }

    @Path("/{track}")
    @PUT
    @Timed
    public Response putOrg(@PathParam("track") String track,
                           @Valid String json) throws IOException {
        DBQuery.Query query = DBQuery.is("name", track);
        Map<String, Object> map = new ObjectMapper().readValue(json, new TypeReference<Map<String, Object>>() {
        });
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getValue() != null) {
                tracks.update(query, DBUpdate.set(entry.getKey(), entry.getValue()));
            }
        }
        return Response.ok(tracks.findOne(DBQuery.is("name", track))).build();
    }

    @Path("/{track}/assignments")
    @PUT
    @Timed
    public Response putAssignment(@PathParam("track") String track,
                                  @Valid String json) throws IOException {
        DBQuery.Query query = DBQuery.is("name", track);
        Track org = tracks.findOne(query);
        Map<String, Object> map = new ObjectMapper().readValue(json, new TypeReference<Map<String, Object>>() {
        });
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            org.addAssignment((String) entry.getValue());
        }
        tracks.update(query, DBUpdate.set("assignments", org.getAssignments()));
        return Response.noContent().build();
    }

    @Path("/{track}")
    @DELETE
    @Timed
    public Response deleteOrg(@PathParam("track") String track) {
        tracks.findAndRemove(DBQuery.is("name", track));
        return Response.ok(tracks.find().toArray()).build();
    }

    @Path("/{track}/assignment/{assignment}")
    @DELETE
    @Timed
    public Response deleteAssignment(@PathParam("track") String track,
                                     @PathParam("assignment") String assignment) {
        DBQuery.Query orgQuery = DBQuery.is("name", track);
        tracks.findOne(orgQuery).removeAssignment(assignment);
        return Response.ok(tracks.findOne(orgQuery).getAssignments()).build();
    }

}